import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Scanner;

import javax.swing.JOptionPane;

/**
 * Copyright (C) 2013  Arun Lee & Bryce Nagareda
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This class checks if there's data saved in the sundial file.<br />
 * If there is, save it and pass true to the GUI so it can enable the
 * button.
 * 
 * @author Arun Lee
 * @author briceorbryce
 * 
 */
public class SundialSave implements Serializable {
	
	/**Serial version UID*/
	private static final long serialVersionUID = 1L;
	
	/**Results of the file.*/
	private ArrayList<String> fileContents = new ArrayList<String>();
	
	/**Calculations object*/
	private SundialMath mathCalculations;
	
	/**
	 * Does double duty:<br />
	 * Lets the GUI know if it should enable the show previous sundial button.<br />
	 * Lets the GUI know that there was a failure because of bad input.
	 */
	private boolean showPreviousSundialButton = true;
	
	/**
	 * The constructor gets called at the beginning of SunDialgui. If a file
	 * called "last run" does not exist, do not enable JButtonShowPreSun.
	 * <br /><br />
	 * If the last run contents are not correct, do not enable the same button.
	 */
	public SundialSave() {
		try {
			checkFile();
			correctSyntax();
		}
		catch (SundialFileErrorException fail) {
			this.showPreviousSundialButton = false;
		}
		catch (FileNotFoundException fail) {
			// happens when there's a folder called "last run"
			this.showPreviousSundialButton = false;
		}
		catch (MonthDayException fail) {
			this.showPreviousSundialButton = false;
		}
	}
	
	/**
	 * The test constructor.
	 * <br />
	 * This constructor is used to test the file.
	 * 
	 * @param results the file
	 */
	public SundialSave (ArrayList<String> results) {
		this.fileContents = results;
		try {
			correctSyntax();
		}
		catch (SundialFileErrorException fail) {
			this.showPreviousSundialButton = false;
		}
		catch (MonthDayException fail) {
			this.showPreviousSundialButton = false;
		}
	}
	
	/**
	 * Checks if the "last run" file exists. If it exists, save the data, else
	 * the method will create it.
	 * <br />
	 * This is so that users can look at their last sundial calculations,
	 * assuming they were valid.
	 * 
	 */
	private void checkFile() throws SundialFileErrorException, FileNotFoundException {
		Scanner checkFile = null;
		try {
			File lastRun = new File("last run");
			if (!fileExists(lastRun)) {

				checkFile = new Scanner(lastRun);
				saveData(checkFile);
			}
			else {
				throw new SundialFileErrorException();
			}
		}
		finally {
			if (checkFile != null) {
				checkFile.close();
			}
		}
	}
	
	/**
	 * Checks if the file exists. Creates it if it doesn't.
	 * 
	 * @note If there's a folder with the same name, the method will NOT create
	 * the file and return false.
	 * 
	 * @return true if it exists
	 */
	protected static boolean fileExists (File lastRun) {
		try {
			return lastRun.createNewFile();
		}
		catch (IOException fail) {
			return false;
		}
	}
	
	/**
	 * Saves the data from the last run.
	 * 
	 * @param checkFile the scanner object
	 */
	private void saveData (Scanner checkFile) {
		while (checkFile.hasNextLine()) {
			this.fileContents.add(checkFile.nextLine());
		}
	}
	
	/**
	 * Makes sure that the contents of the file have the correct syntax.
	 * <br />
	 * Alternatively, it checks to make sure that the user inputted the correct
	 * syntax.
	 * 
	 * @param checkFile checks if reading from file or from GUI
	 * @throws SundialFileErrorException file doesn't exist
	 * @throws MonthDayException invalid day and month
	 */
	private void correctSyntax() throws SundialFileErrorException, MonthDayException {
		checkFileLines();
		checkOrientation();
		checkValidContents();
	}
	
	/**
	 * Checks the file to make sure it has 5 lines.
	 * 
	 * @throws SundialFileErrorException the file didn't have the 5 lines required.
	 */
	private void checkFileLines () throws SundialFileErrorException {
		if (this.fileContents.size() != 5) {
			throw new SundialFileErrorException();
		}
	}
	
	/**
	 * Checks to make sure sundial is either in horizontal or equitorial mode.
	 * <br />
	 * If neither, throw exception.
	 */
	private void checkOrientation() throws SundialFileErrorException {
		if (!this.fileContents.get(4).toLowerCase().equals("horizontal") &&
				(!this.fileContents.get(4).toLowerCase().equals("equitorial")) ) {
			throw new SundialFileErrorException();
		}
	}
	
	/**
	 * Checks arraylist to make sure contents are valid.<br />
	 * If they are, set the calculations to the new sundial.
	 * 
	 * @throws SundialFileErrorException
	 * @throws MonthDayException
	 */
	private void checkValidContents() throws SundialFileErrorException, MonthDayException {
		try {
			double testLatitude = Double.valueOf(this.fileContents.get(0)).doubleValue(); // latitude
			double testLongitude = Double.valueOf(this.fileContents.get(1)).doubleValue(); // longitude
			int testDay = Integer.valueOf(this.fileContents.get(2)).intValue(); // day
			setMathCalculations(new SundialMath(testLatitude, testLongitude, testDay, this.fileContents.get(3)));
		}
		catch (NumberFormatException fail) {
			throw new SundialFileErrorException();
		}
	}
	
	/**
	 * Saves the current sundial to a file.
	 * 
	 * @param calc the sundialmath obj
	 * @param month month currently selected
	 * @param orientation orientation currently select
	 */
	public void saveToFile (SundialMath calc, String month, String orientation) {
		Thread worker = new Thread(new ThreadWriteToFile(calc, month, orientation));
		worker.start();
	}

	/**
	 * Getter to let the GUI know if it should enable the button.
	 * @return true if the file exists and valid
	 */
	public boolean getShowPreviousSundialButton() {
		return this.showPreviousSundialButton;
	}
	
	/**
	 * Getter for SundialMath object.
	 * @return the mathCalculations
	 */
	public SundialMath getMathCalculations() {
		return this.mathCalculations;
	}

	/**
	 * @param mathCalculations the mathCalculations to set
	 */
	public void setMathCalculations(SundialMath mathCalculations) {
		this.mathCalculations = mathCalculations;
	}
	
	
	
	
	/**
	 * Creates another thread to write out to the last run file.
	 * 
	 * @author Arun Lee
	 * @author briceorbryce
	 *
	 */
	private class ThreadWriteToFile implements Runnable {
		
		/**Math object*/
		private SundialMath math;
		/**The month*/
		private String month;
		/**The orientation*/
		private String orientation;
		
		/**
		 * @param math math
		 * @param month month
		 * @param orientation orientation
		 */
		public ThreadWriteToFile (SundialMath math, String month, String orientation) {
			this.math = math;
			this.month = month;
			this.orientation = orientation;
		}
		
		@Override
		public void run() {
			try{
				FileWriter fw = new FileWriter("last run");
				fw.write(String.valueOf(this.math.getDoubleLatitude()) + "\n");
				fw.write(String.valueOf(this.math.getDoubleLongitude()) + "\n");
				fw.write(String.valueOf(this.math.getDay()) + "\n");
				fw.write(this.month + "\n");
				fw.write(this.orientation + "\n");
				
				fw.close();
			}
			catch (IOException fail) {
				JOptionPane.showMessageDialog(null, fail);
			}
		}
		
	}
}

